package com.androwit.engine.mapping;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Basic implementation that enables the use of super entities definitions
 * 
 * @author Zied Hamdi Founder of http://1vu.fr,  hamdi
 */
public abstract class AbstractMappingFieldsDefiner<J, K, V> implements MappingFieldsDefiner<J, K, V> {
	protected List<AbstractMappingFieldsDefiner<J, K, V>> parentDefiners = new ArrayList<AbstractMappingFieldsDefiner<J, K, V>>();

	@SuppressWarnings("unchecked")
	public AbstractMappingFieldsDefiner(AbstractMappingFieldsDefiner<J, K, V>... superInstances) {
		addMappingFieldsDefiners(superInstances);
		addMappingFieldsDefiners(this);
	}

	/**
	 * addMappingFieldsDefiners
	 * 
	 * @param definers
	 */
	protected void addMappingFieldsDefiners(AbstractMappingFieldsDefiner<J, K, V>... definers) {
		for (AbstractMappingFieldsDefiner<J, K, V> abstractMappingFieldsDefiner : definers) {
			addParentDefiner(abstractMappingFieldsDefiner);
		}
	}

	/**
	 * 
	 * adds a parent mapping fields definer, this should be an initializing operation
	 * 
	 * @param abstractMappingFieldsDefiner
	 */
	public void addParentDefiner(AbstractMappingFieldsDefiner<J, K, V> abstractMappingFieldsDefiner) {
		this.parentDefiners.add(abstractMappingFieldsDefiner);
	}

	@SuppressWarnings("unchecked")
	@Override
	public MappingField<J, K, V>[] getMappedFields(Map<K, V> values) {
		List<MappingField<J, K, V>> toReturn = new ArrayList<MappingField<J, K, V>>();
		for (AbstractMappingFieldsDefiner<J, K, V> mappingFieldDefiner : parentDefiners) {
			toReturn.addAll(asList(mappingFieldDefiner.getLocalMappingFields(values)));
		}
		// toReturn.addAll(asList(getLocalMappingFields(values)));
		return toReturn.toArray(new MappingField[0]);
	}

	@SuppressWarnings("unchecked")
	@Override
	public MappingField<J, K, V>[] getMappingFields(Object toPersist) {
		List<MappingField<J, K, V>> toReturn = new ArrayList<MappingField<J, K, V>>();
		for (AbstractMappingFieldsDefiner<J, K, V> mappingFieldDefiner : parentDefiners) {
			// System.out.println("local mapping fields of " + mappingFieldDefiner + " "
			// + asList(mappingFieldDefiner.getLocalMappingFields(toPersist)));

			toReturn.addAll(asList(mappingFieldDefiner.getLocalMappingFields(toPersist)));
		}
		toReturn.addAll(asList(getLocalMappingFields(toPersist)));
		return toReturn.toArray(new MappingField[0]);
	}

	/**
	 * asList
	 * 
	 * @param mappingFields
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected List<MappingField<J, K, V>> asList(MappingField<J, K, V>[] mappingFields) {
		if (mappingFields == null || mappingFields.length == 0)
			return Collections.EMPTY_LIST;
		return Arrays.asList(mappingFields);
	}

	/**
	 * 
	 * returns Local Mapping Fields for this level
	 * 
	 * @param values
	 * @return
	 */
	protected abstract MappingField<J, K, V>[] getLocalMappingFields(Map<K, V> values);

	/**
	 * 
	 * returns Local Mapping Fields for this level
	 * 
	 * @param topersist
	 * @return
	 */
	protected abstract MappingField<J, K, V>[] getLocalMappingFields(Object topersist);

}
